package com.calvin.study.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Objects;

import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import com.calvin.study.config.UploadConfig;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FileUploadUtil {

	private static final DateFormat TIME_FORMAT = new SimpleDateFormat("HHmmssSSS");
	private static final DateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmssSSS");

	public static String uploadImage(String imageUrl) throws Exception {
		return upload(imageUrl, MimeTypeUtils.IMAGE_FORMAT);
	}

	public static String uploadImage(MultipartFile file) throws Exception {
		return upload(file, file.getOriginalFilename(), MimeTypeUtils.IMAGE_FORMAT);
	}

	public static String uploadImage(MultipartFile file, String fileName) throws Exception {
		return upload(file, fileName, MimeTypeUtils.IMAGE_FORMAT);
	}
	
	public static String uploadFile(String url) throws Exception{
		//无法获取文件类型，即生成文件名后缀未知
		return null;
	}

	public static String uploadFile(MultipartFile file) throws Exception {
		return upload(file, file.getOriginalFilename(), MimeTypeUtils.DEFAULT_ALLOWED_FORMAT);
	}

	public static String uploadFile(MultipartFile file, String fileName) throws Exception {
		return upload(file, fileName, MimeTypeUtils.DEFAULT_ALLOWED_FORMAT);
	}

	/**
	 * 文件上传（url转MultipartFile）
	 * 
	 * @param url
	 * @param allowedFormatType
	 * @return
	 * @author Wangh
	 * @version
	 * @throws Exception
	 * @date 2024-08-30
	 */
	private static String upload(String url, String[] allowedFormatType) throws Exception {
		MultipartFile multipartFile = convertUrlToMultipartFile(url, createFileName());
		return upload(multipartFile, multipartFile.getOriginalFilename(), allowedFormatType);
	}

	/**
	 * 文件上传
	 * 
	 * @param multipartFile
	 * @param fileName
	 * @param allowedFormatType
	 * @return
	 * @throws Exception
	 * @author Wangh
	 * @version
	 * @date 2024-08-30
	 */
	private static String upload(MultipartFile multipartFile, String fileName, String[] allowedFormatType)
			throws Exception {
		String fileSaveDir = UploadConfig.getFileSaveDir();
		log.info("上传图片开始");
		try {
			if (null == multipartFile) {
				throw new Exception("请选择要上传的文件");
			}
			// file合法性校验
			assertAllowed(multipartFile, allowedFormatType);
			// 生成目录地址
			String fileDir = createFilePath(fileSaveDir);
			// 文件完整路径
			String absolutePath = getAbsoluteFile(fileDir, fileName).getAbsolutePath();
			log.info("[FileUploadUtils][upload] file absolute path: " + absolutePath);
			// 本地上传(写入到磁盘)
			multipartFile.transferTo(new File(absolutePath));

			return fileName;
		} catch (Exception e) {
			throw new Exception(StrUtil.format("生成图片错误：{}", e.getMessage()));
		}
	}

	/**
	 * 将MultipartFile转换为File
	 * 
	 * @param mFile
	 * @return
	 * @throws Exception
	 * @author Wangh
	 * @version
	 * @date 2024-08-29
	 */
	public static File convertMultipartFileToFile(MultipartFile multipartFile) throws Exception {
		File tempFile = null;
		try {
			if (null == multipartFile) {
				return tempFile;
			}
			String originalFilename = multipartFile.getOriginalFilename();// 获取源文件名
			tempFile = File.createTempFile(originalFilename, null);// 创建临时文件
			multipartFile.transferTo(tempFile);// 将临时文件写入磁盘
		} catch (Exception e) {
			throw new Exception("MultipartFile转换File出错：" + e.getMessage());
		}
		return tempFile;
	}

	/**
	 * Url转MultipartFile
	 * 
	 * @param url
	 * @param fileName
	 * @return
	 * @author Wangh
	 * @version
	 * @throws Exception
	 * @date 2024-08-30
	 */
	public static MultipartFile convertUrlToMultipartFile(String url, String fileName) throws Exception {
		log.info("图片连接：" + url);
		HttpURLConnection connection = null;
		try {
			connection = (HttpURLConnection) new URL(url).openConnection();
			connection.setRequestMethod("GET");
			connection.setReadTimeout(5000);
			connection.setConnectTimeout(5000);
			if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
				return convertInputStreamToMultipartFile(connection.getInputStream(), fileName);
			}
		} catch (Exception e) {
			throw new Exception("图片链接转换文件错误：" + e.getMessage());
		} finally {
			if (null != connection) {
				connection.disconnect();
			}
		}
		return null;
	}

	/**
	 * InputStream转MultipartFile
	 * 
	 * @param inputStream
	 * @param fileName
	 * @return
	 * @throws IOException
	 * @author Wangh
	 * @version
	 * @date 2024-08-30
	 */
	private static MultipartFile convertInputStreamToMultipartFile(InputStream inputStream, String fileName)
			throws IOException {
		byte[] byteArray = inputStreamToByteArray(inputStream);
		return new MockMultipartFile(fileName, fileName, "application/octet-stream", byteArray);
	}

	/**
	 * 文件流转字节数组
	 * 
	 * @param inputStream
	 * @return
	 * @throws IOException
	 * @author Wangh
	 * @version
	 * @date 2024-08-30
	 */
	private static byte[] inputStreamToByteArray(InputStream inputStream) throws IOException {
		try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
			byte[] buffer = new byte[1024];
			int length;
			while ((length = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, length);
			}
			return outputStream.toByteArray();
		}
	}

	/**
	 * 获取文件绝对地址
	 * 
	 * @param fileDir  文件路径 例如：/data/image/
	 * @param fileName 文件名
	 * @return
	 * @throws IOException
	 */
	public static final File getAbsoluteFile(String fileDir, String fileName) throws IOException {
		File absolutePath = new File(fileDir + fileName);
		if (!absolutePath.exists()) {// 判断当前路径
			if (!absolutePath.getParentFile().exists()) {
				absolutePath.getParentFile().mkdirs();
			}
		}
		return absolutePath;
	}

	/**
	 * 生成文件名
	 * 
	 * @param type
	 * @return
	 */
	private static String createFileName() {
		return StrUtil.format("{}.{}", yyyyMMddHHmmss.format(System.currentTimeMillis()),
				MimeTypeUtils.IMAGE_FORMAT[2]);
	}

	/**
	 * 生成文件名
	 * 
	 * @param type
	 * @return
	 */
	public static String createFileName(MultipartFile file) {
		return StrUtil.format("{}.{}", yyyyMMddHHmmss.format(System.currentTimeMillis()), getSuffix(file));
	}

	/**
	 * 生成文件名
	 * 
	 * @param namePrefix 可制定前缀
	 * @param file
	 * @return 文件名，prefix+202xxxxxxxxxxxxxx.png
	 * @author Wangh
	 * @version
	 * @date 2024-08-30
	 */
	public static String createFileName(String namePrefix, MultipartFile file) {
		return StrUtil.format("{}.{}", namePrefix + yyyyMMddHHmmss.format(System.currentTimeMillis()), getSuffix(file));
	}

	/**
	 * 创建文件目录
	 */
	public static String createFilePath(String saveDir) {
		File dir = new File(saveDir);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return saveDir;
	}

	/**
	 * 获取文件名的后缀
	 * 
	 * @param file 表单文件
	 * @return 后缀名
	 */
	public static final String getSuffix(MultipartFile file) {
		String extension = FileNameUtil.getSuffix(file.getOriginalFilename());
		if (StrUtil.isEmpty(extension)) {
			extension = MimeTypeUtils.getFormatType(Objects.requireNonNull(file.getContentType()));
		}
		return extension;
	}

	/**
	 * 文件校验
	 * 
	 * @param file
	 * @param allowedFormatType
	 * @throws Exception
	 * @author Wangh
	 * @version
	 * @date 2024-08-30
	 */
	public static final void assertAllowed(MultipartFile file, String[] allowedFormatType) throws Exception {
		// 校验文件大小
		long size = file.getSize();
		if (size > UploadConfig.getFileMaxSize()) {
			throw new Exception(StrUtil.format("上传文件最大不超过{}MB", UploadConfig.getFileMaxSize() / 1024 / 1024));
		}

		// 校验文件格式合法性
		String fileName = file.getOriginalFilename();
		String suffix = getSuffix(file);
		if (allowedFormatType != null && !isAllowedFormatType(suffix, allowedFormatType)) {
			throw new Exception(
					StrUtil.format("文件[{}]后缀[{}]不正确，请上传{}格式", fileName, suffix, Arrays.toString(allowedFormatType)));
		}
	}

	/**
	 * 判断MIME类型是否是允许的MIME类型
	 *
	 * @param suffix            后缀
	 * @param allowedFormatType 格式类型
	 * @return
	 */
	public static final boolean isAllowedFormatType(String suffix, String[] allowedFormatType) {
		for (String str : allowedFormatType) {
			if (str.equalsIgnoreCase(suffix)) {
				return true;
			}
		}
		return false;
	}
}
